import {
	resolve,
	relative,
} from 'node:path';
import chalk from 'chalk';
import webpack from 'webpack';
import {
	merge,
} from 'webpack-merge';
import {
	extend,
	copyReplace,
	copyCover,
	copyCustom,
	dateFormat,
	getEnvFiles,
	getFileEnvObj,
	readJsonSync,
} from './common.js';
import build from '../command/build.js';
import lib from '../command/lib.js';
import serve from '../command/serve.js';
import commonConfig from '../webpack/webpack.common.js';
import buildConfig from '../webpack/webpack.build.js';
import libConfig from '../webpack/webpack.lib.js';
import serveConfig from '../webpack/webpack.serve.js';
import load from './importTypeScript.js';

const commandMap={
	build,
	lib,
	serve,
	config:build,
};
const configMap={
	build:buildConfig,
	lib:libConfig,
	serve:serveConfig,
	config:buildConfig,
};
class Service{
	constructor(baseDir,args){
		this.baseDir=baseDir;
		this.packageJson=readJsonSync(`${baseDir}/package.json`);
		this.args=args;
		this.command=(args._&&args._[0])||'build';
	}
	run(){
		const commandFn=commandMap[this.command]();
		this.getConfig().then((param) => {
			commandFn(param);
		});
	}
	//region 获取webpack配置信息
	getConfig(){
		return this.getUserConfig().then((config) => {
			const scope={
				baseDir:this.baseDir,
				packageJson:this.packageJson,
				args:this.args,
				command:this.command,
				config,
			};
			return Promise.all([
				commonConfig(scope),
				configMap[scope.command](scope),
				scope.config.webpackConfig,
			]).then((webpackConfigs) => {
				return {
					webpackConfig:merge(webpackConfigs),
					...scope,
				};
			});
		});
	}
	//endregion
	//region 获取用户配置信息
	getUserConfig(){
		const scope={
			returnCheck(val){
				return val;
			},
			baseDir:this.baseDir,
			packageJson:this.packageJson,
			args:this.args,
			command:this.command,
			utils:{
				extend,
				copyReplace,
				copyCover,
				copyCustom,
				dateFormat,
			},
		};
		const configPath=resolve(scope.baseDir,'powerful.config.ts');
		//region 获取项目配置(递归extends继承链)
		const getCustomConfig=(pathOrFn) => {
			let promise;
			if(typeof pathOrFn==='string'){
				promise=load(scope.baseDir,pathOrFn).then(({default:fn}) => {
					return fn(scope);
				});
			}else if(typeof pathOrFn==='function'){
				promise=pathOrFn(scope);
			}else{
				console.error(`${pathOrFn} 不是有效的配置文件!`);
				promise=Promise.reject();
			}
			return promise.then((config) => {
				const {extendConfigs}=config;
				if(Array.isArray(extendConfigs)&&extendConfigs.length){
					return Promise.all(
						extendConfigs.map((extendConfig) => {
							return getCustomConfig(extendConfig)
						})
					).then((extendConfigs) => {
						const mergeConfig={};
						for(const extendConfig of extendConfigs){
							if(extendConfig!=null){
								extend(mergeConfig,extendConfig);
							}
						}
						return extend(mergeConfig,config);
					});
				}
				return config;
			});
		};
		//endregion
		return getCustomConfig(configPath).then(async (config) => {
			const {
				injectFns,
				...baseConfig
			}=config;
			//region 收集所有环境变量文件
			const envFiles=await getEnvFiles({
				baseDir:scope.baseDir,
				envPath:baseConfig.envPath,
				mode:scope.args.mode,
			});
			const envDependencies=[];
			const envArr=[]
			for(const envFile of envFiles){
				const relativePath=relative(scope.baseDir,envFile);
				if(relativePath.startsWith('node_modules')){
					envArr.push(getFileEnvObj(envFile));
				}else{
					envDependencies.push(envFile);
					envArr.push(envFile);
				}
			}
			//endregion
			//region getAllFileEnvObj
			const getAllFileEnvObj=() => {
				const envObj={};
				for(const envItem of envArr){
					if(typeof envItem==='string'){
						getFileEnvObj(envItem,envObj);
					}else{
						for(const key of Object.keys(envItem)){
							envObj[key]=envItem[key];
						}
					}
				}
				return envObj;
			};
			//endregion
			const definePlugin={};
			//region 动态环境变量
			const envObj=getAllFileEnvObj();
			for(const key of Object.keys(envObj)){
				definePlugin['process.env.'+key]=webpack.DefinePlugin.runtimeValue(() => {
					return JSON.stringify(getAllFileEnvObj()[key]);
				},{
					fileDependencies:envDependencies,
				});
				process.env[key]=envObj[key];
			}
			//endregion
			const extraEnvObj={};
			//region 静态环境变量
			for(const key of Object.keys(baseConfig.extraEnv)){
				const val=JSON.stringify(baseConfig.extraEnv[key]);
				definePlugin['process.env.'+key]=val;
				extraEnvObj[key]=val;
				process.env[key]=baseConfig.extraEnv[key];
			}
			//endregion
			//region 整个环境变量对象
			definePlugin['process.env']=webpack.DefinePlugin.runtimeValue(() => {
				const envObj=getAllFileEnvObj();
				for(const key of Object.keys(envObj)){
					envObj[key]=JSON.stringify(envObj[key]);
				}
				for(const key of Object.keys(extraEnvObj)){
					envObj[key]=extraEnvObj[key];
				}
				envObj.NODE_ENV=JSON.stringify(process.env.NODE_ENV);
				return envObj;
			},{
				fileDependencies:envDependencies,
			});
			//endregion
			console.log('生效的环境变量',envFiles);
			const injectFnParam=Object.assign({},baseConfig,scope);
			return Promise.all(injectFns.map((injectFn) => injectFn(injectFnParam))).then((injectConfigs) => {
				const finalConfig=extend({definePlugin},baseConfig);
				for(const injectConfig of injectConfigs){
					if(injectConfig!=null){
						//region 处理注入进来的extraEnv
						if(injectConfig.extraEnv){
							for(const key of Object.keys(injectConfig.extraEnv)){
								const val=JSON.stringify(injectConfig.extraEnv[key]);
								definePlugin['process.env.'+key]=val;
								extraEnvObj[key]=val;
								process.env[key]=injectConfig.extraEnv[key];
							}
						}
						//endregion
						extend(finalConfig,injectConfig);
					}
				}
				if(finalConfig.vConsole.active){
					//生成模式打印完成错误信息
					finalConfig.definePlugin.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__='true';
					finalConfig.definePlugin.__VUE_PROD_DEVTOOLS__=`${finalConfig.vConsole.vue}`;
					finalConfig.definePlugin.__POWERFUL_VCONSOLE_VUE__=`${finalConfig.vConsole.vue}`;
				}
				console.log('生成最终的配置!');
				if(finalConfig.debug){
					console.log(chalk.green(JSON.stringify(finalConfig,null,'    ')));
				}
				return finalConfig;
			});
		});
	}
	//endregion
}

export default Service;
